package client

import (
	"context"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"math/big"
	"shendaNFT/pkg/model"
)

// GetBlockNumber 获取最近的区块高度
func (c *EthClient) GetBlockNumber() (uint64, error) {
	ctx := context.Background()
	return c.client.BlockNumber(ctx)
}

// GetBlockByHash 根据区块哈希获取区块数据
func (c *EthClient) GetBlockByHash(blockHash string) (*model.Block, error) {
	ctx := context.Background()
	// 调用内部请求获取数据
	block, err := c.client.BlockByHash(ctx, common.HexToHash(blockHash))
	if err != nil {
		return nil, err
	}
	return convertBlock(block), nil
}

// GetBlockByNumber 根据区块高度获取区块
func (c *EthClient) GetBlockByNumber(blockNumber int64) (*model.Block, error) {
	ctx := context.Background()
	// 调用内部请求获取数据
	block, err := c.client.BlockByNumber(ctx, big.NewInt(blockNumber))
	if err != nil {
		return nil, err
	}
	return convertBlock(block), nil
}

// convertBlock 转换区块数据格式
func convertBlock(block *types.Block) *model.Block {
	return &model.Block{
		BlockHash:   block.Hash().String(),
		BlockNumber: block.Number().Uint64(),
		Timestamp:   block.Time(),
		Txs:         block.Transactions().Len(),
		Miner:       block.Coinbase().String(),
		Size:        block.Size().TerminalString(),
		GasUsed:     block.GasUsed(),
		GasLimit:    block.GasLimit(),
	}
}

func (c *EthClient) GetTxInBlock(blockNumber int64, txIndex uint) (*model.Transaction, error) {
	ctx := context.Background()
	var (
		block   *types.Block
		receipt *types.Receipt
		sender  common.Address
		err     error
	)
	block, err = c.client.BlockByNumber(ctx, big.NewInt(blockNumber))
	if err != nil {
		return nil, err
	}
	tx, err := c.client.TransactionInBlock(ctx, block.Hash(), txIndex)
	if err != nil {
		return nil, err
	}
	receipt, err = c.client.TransactionReceipt(ctx, tx.Hash())
	if err != nil {
		return nil, err
	}
	sender, err = c.client.TransactionSender(ctx, tx, receipt.BlockHash, receipt.TransactionIndex)
	if err != nil {
		return nil, err
	}
	block, err = c.client.BlockByNumber(ctx, receipt.BlockNumber)
	if err != nil {
		return nil, err
	}
	var to string
	if tx.To() == nil {
		to = receipt.ContractAddress.String()
	} else {
		to = tx.To().String()
	}
	return &model.Transaction{
		TxHash:      tx.Hash().String(),
		Status:      receipt.Status,
		BlockNumber: receipt.BlockNumber.Uint64(),
		Timestamp:   block.Time(),
		From:        sender.String(),
		To:          to,
		Value:       tx.Value().String(),
		GasUsed:     receipt.GasUsed,
	}, nil
}

func (c *EthClient) GetTx(txHash string) (*model.Transaction, error) {
	ctx := context.Background()
	tx, isPending, err := c.client.TransactionByHash(ctx, common.HexToHash(txHash))
	if err != nil {
		return nil, err
	}
	if !isPending {
		var receipt *types.Receipt
		receipt, err = c.client.TransactionReceipt(ctx, common.HexToHash(txHash))
		if err != nil {
			return nil, err
		}
		var sender common.Address
		sender, err = c.client.TransactionSender(ctx, tx, receipt.BlockHash, receipt.TransactionIndex)
		if err != nil {
			return nil, err
		}
		var block *types.Block
		block, err = c.client.BlockByNumber(ctx, receipt.BlockNumber)
		if err != nil {
			return nil, err
		}
		var to string
		if tx.To() == nil {
			to = receipt.ContractAddress.String()
		} else {
			to = tx.To().String()
		}
		return &model.Transaction{
			TxHash:      tx.Hash().String(),
			Status:      receipt.Status,
			BlockNumber: receipt.BlockNumber.Uint64(),
			Timestamp:   block.Time(),
			From:        sender.String(),
			To:          to,
			Value:       tx.Value().String(),
			GasUsed:     receipt.GasUsed,
		}, nil
	} else {
		return &model.Transaction{
			TxHash: tx.Hash().String(),
		}, nil
	}
}

// GetBalance 获取账户余额
func (c *EthClient) GetBalance(address common.Address) (*big.Int, error) {
	ctx := context.Background()
	return c.client.BalanceAt(ctx, address, nil)
}
